#include "Arduino.h"    
#include <OneButton.h>       
#include <WiFi.h>
#include <sddc.h>
#include <cJSON.h>
#include <Wire.h>


#define SDDC_CFG_PORT             680U             // SDDC 协议使用的端口号
#define CAMERA_MODEL_AI_THINKER                    // 安信可 esp-32 有 PSRAM
#define PIN_INPUT 0                                // 选择 IO0 进行控制
#define ESP_TASK_STACK_SIZE       4096
#define ESP_TASK_PRIO             25

static sddc_t *g_sddc;
int    g_flag = SDDC_FALSE;
TaskHandle_t g_BME_TaskHandle;
TaskHandle_t g_LUX_TaskHandle;

static unsigned char Re_buf[15],counter = 0;   
static unsigned char sign = 0;
static uint32_t Lux;

static const char* ssid = "EOS-000045";            // WiFi 名
static const char* password = "1234567890";        // WiFi 密码


typedef struct
{
    uint32_t P;
    uint16_t Temp;
    uint16_t Hum;
    uint16_t Alt;
} bme;

OneButton button(PIN_INPUT, true);
/*
 * BME 数据接收函数
 */
void BME_serial_event() 
{
        while (Serial2.available()) {   
        Re_buf[counter]=(unsigned char)Serial2.read(); 
        if(counter==0&&Re_buf[0]!=0x5A) return;      // 检查帧头         
        counter++;       
        if(counter==15)                              //接收到数据
        {    
           counter=0;                                //重新赋值，准备下一帧数据的接收 
           sign=1;
        } 
    }
}
/*
 * LUX 数据接收函数
 */
void LUX_serial_event() 
{
        while (Serial2.available()) {   
        Re_buf[counter]=(unsigned char)Serial2.read();

        if(counter==0&&Re_buf[0]!=0x5A) return;      // 检查帧头         
        counter++;       
        if(counter == 9)                             //接收到数据
        {    
           counter=0;                                //重新赋值，准备下一帧数据的接收 
           sign=1;
        }    
    }
}
/*
 * BME 数据解析函数
 */
int BME_parser(cJSON **value)
{
    unsigned char i=0,sum=0;
    uint16_t data_16[2]={0};
    bme Bme;
    BME_serial_event();
    if(sign)
    {   
        for(i=0;i<14;i++)
        sum+=Re_buf[i]; 
        if(sum==Re_buf[i] )                           //检查帧头，帧尾
        { 
            Bme.Temp=(Re_buf[4]<<8)|Re_buf[5];
            data_16[0]=(Re_buf[6]<<8)|Re_buf[7];
            data_16[1]=(Re_buf[8]<<8)|Re_buf[9];
            Bme.P=(((uint32_t)data_16[0])<<16)|data_16[1];
            Bme.Hum=(Re_buf[10]<<8)|Re_buf[11];
            Bme.Alt=(Re_buf[12]<<8)|Re_buf[13];      
            Serial.print("Temp: ");                    // 温度
            Serial.print( (float)Bme.Temp/100);
            cJSON_AddNumberToObject(*value, "Temp:", (float)Bme.Temp/100);
            Serial.print(" DegC  PRESS : ");           // 气压
            Serial.print( ((float)Bme.P)/100);
            cJSON_AddNumberToObject(*value, "PRESS", (float)Bme.P/100);
            Serial.print(" Pa  HUM : ");               // 湿度
            Serial.print( (float)Bme.Hum/100);
            cJSON_AddNumberToObject(*value, "HUM", (float)Bme.Hum/100);
            Serial.print(" % ALT:");                   // 海拔
            Serial.print( Bme.Alt);
            cJSON_AddNumberToObject(*value, "ALT", (float)Bme.Alt/100);
            Serial.println("m");
            sign=0;      
            return SDDC_TRUE; 
        }
    } 
    return SDDC_FALSE;
}
/*
 * LUX 数据解析函数
 */
int LUX_parser(cJSON *value) 
{
    unsigned char i=0,sum=0;
    uint16_t data_16[2]={0};
    bme Bme;
  
    LUX_serial_event();
  
    if(sign)
    {   
        for(i=0;i<8;i++)
        sum+=Re_buf[i]; 
        if(sum==Re_buf[i] )        //检查帧头，帧尾
        {  
           Lux = (Re_buf[4]<<24)|(Re_buf[5]<<16)|Re_buf[6]|Re_buf[7];
           Serial.print("LUX : ");
           Serial.print((uint32_t)Lux);
           cJSON_AddNumberToObject(value, "LUX", (uint32_t)Lux);
           sign=0;   
           return SDDC_TRUE;      
        }
    } 
    return SDDC_FALSE;
}
/*
 * BME 任务函数
 */
static void esp_BME_task(void *arg)
{  
    cJSON *BME_value;
    BME_value = cJSON_CreateObject();
    sddc_return_if_fail(BME_value);
    char  *msg;
    
    while(g_flag == SDDC_TRUE)
    {
        BME_value = cJSON_CreateObject();
        sddc_return_if_fail(BME_value);
      
        // 获取 BME 数据
        if(BME_parser(&BME_value) == SDDC_TRUE)
        {
            // 发送数据给 EdgerOS
            msg = cJSON_Print(BME_value);
            if (sddc_broadcast_message(g_sddc, msg, strlen(msg), 1, SDDC_FALSE, NULL) != 0)
            {
            sddc_printf("send msg error!\n");
            break;
            }
            Serial.printf("BME data 发送\n");
         }
         cJSON_Delete(BME_value);
         delay(100);
    }       
    Serial.printf("BME data OFF\n");

    cJSON_free(msg);
    
    vTaskDelete(NULL);
}
/*
 * LUX 任务函数
 */
static void esp_LUX_task(void *arg)
{  
    cJSON *LUX_value;
    LUX_value = cJSON_CreateObject();
    sddc_return_if_fail(LUX_value);
    char  *msg;
     
    while(g_flag == SDDC_TRUE)
    {
        LUX_value = cJSON_CreateObject();
        sddc_return_if_fail(LUX_value);
      
        // 获取 LUX 数据
        if(LUX_parser(LUX_value) == SDDC_TRUE)
        {
           // 发送数据给 EdgerOS
           msg = cJSON_Print(LUX_value);
           if (sddc_broadcast_message(g_sddc, msg, strlen(msg), 1, SDDC_FALSE, NULL) != 0)
           {
           sddc_printf("send msg error!\n");
           break;
           }
           Serial.printf("LUX data 发送\n");
       }
       cJSON_Delete(LUX_value);
       delay(100);
    }       
    Serial.printf("LUX data OFF\n");

    cJSON_free(msg);
    
    vTaskDelete(NULL);
}
/*
 * 事件响应函数
 */
static sddc_bool_t iot_pi_on_message(sddc_t *sddc, const uint8_t *uid, const char *message, size_t len)
{
    cJSON *root = cJSON_Parse(message);

    static cJSON *item;
    char  *str;
    
    sddc_return_value_if_fail(root, SDDC_TRUE);
    str = cJSON_Print(root);
    sddc_printf("收到的命令: %s\n", str);
    
    // 判断收到的是否是 GY39 传感器命令
    if((item = cJSON_GetObjectItem(root, "GY39")) != NULL)
    {
        Serial.printf("确认 GY39 命令\n");
        sddc_return_value_if_fail(item, SDDC_TRUE);

        // 判断开关命令
        if((strcmp(item->valuestring, "OFF")) == 0)
        {
            g_flag = SDDC_FALSE;
            delay(200);    
            Serial2.write(0XA5); 
            Serial2.write(0X00);    //停止发送数据
            Serial2.write(0XA5); 
        }

        // 创建传感器任务，选择并获取传感器的数据并发送给 EdgerOS
        if((strcmp(item->valuestring, "BME")) == 0)
        {
            sddc_printf("BME command ！\n");
            if(g_LUX_TaskHandle != NULL)
            {
             vTaskDelete(g_LUX_TaskHandle);
            }
            // 给模块发送命令
            delay(200);    
            Serial2.write(0XA5); 
            Serial2.write(0X02);    //初始化,连续输出温度、气压、湿度、海拔
            Serial2.write(0XA7); 
          
            g_flag = SDDC_TRUE;
            xTaskCreate(esp_BME_task, "BME_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, &g_BME_TaskHandle);
        }
        if ((strcmp(item->valuestring, "LUX")) == 0)
        {
            sddc_printf("LUX command ！\n");
            if(g_BME_TaskHandle != NULL)
            {
                vTaskDelete(g_BME_TaskHandle);
            }
            // 给模块发送命令
            delay(200);    
            Serial2.write(0XA5); 
            Serial2.write(0X01);    //初始化,连续输出光强
            Serial2.write(0XA6); 
          
            g_flag = SDDC_TRUE;
            xTaskCreate(esp_LUX_task, "LUX_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, &g_LUX_TaskHandle);
        }
    }else
    {
        sddc_printf("Unknown command ！\n");
    }
    
error:
    cJSON_Delete(root);

    return SDDC_TRUE;
}
/*
 * 接收消息请求时的回调函数
 */
static void iot_pi_on_message_ack(sddc_t *sddc, const uint8_t *uid, uint16_t seqno)
{
}
/*
 * 丢失消息时的回调函数
 */
static void iot_pi_on_message_lost(sddc_t *sddc, const uint8_t *uid, uint16_t seqno)
{
}
/*
 * EdgerOS 断连时的回调函数
 */
static void iot_pi_on_edgeros_lost(sddc_t *sddc, const uint8_t *uid)
{
}
/*
 * 接收更新请求时的回调函数
 */
static sddc_bool_t iot_pi_on_update(sddc_t *sddc, const uint8_t *uid, const char *udpate_data, size_t len)
{
    cJSON *root = cJSON_Parse(udpate_data);
    char *str;

    sddc_return_value_if_fail(root, SDDC_FALSE);

    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);

    sddc_printf("iot_pi_on_update: %s\n", str);
    cJSON_free(str);

    cJSON_Delete(root);

    return SDDC_TRUE;

error:
    cJSON_Delete(root);

    return SDDC_FALSE;
}
/*
 * 接受邀请请求时的回调函数
 */
static sddc_bool_t iot_pi_on_invite(sddc_t *sddc, const uint8_t *uid, const char *invite_data, size_t len)
{
    cJSON *root = cJSON_Parse(invite_data);
    char *str;

    sddc_return_value_if_fail(root, SDDC_FALSE);

    str = cJSON_Print(root);
    sddc_goto_error_if_fail(str);
    
    sddc_printf("iot_pi_on_invite: %s\n", str);
    cJSON_free(str);

    cJSON_Delete(root);

    return SDDC_TRUE;

error:
    cJSON_Delete(root);

    return SDDC_FALSE;
}
/*
 * 发送邀请后的回调函数
 */
static sddc_bool_t iot_pi_on_invite_end(sddc_t *sddc, const uint8_t *uid)
{
    return SDDC_TRUE;
}
/*
 * 创建 REPORT 数据
 */
static char *iot_pi_report_data_create(void)
{
    cJSON *root;
    cJSON *report;
    char *str;

    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, NULL);

    report = cJSON_CreateObject();
    sddc_return_value_if_fail(report, NULL);

    cJSON_AddItemToObject(root, "report", report);
    cJSON_AddStringToObject(report, "name",   "IoT Pi");
    cJSON_AddStringToObject(report, "type",   "device");
    cJSON_AddBoolToObject(report,   "excl",   SDDC_FALSE);
    cJSON_AddStringToObject(report, "desc",   "翼 辉 IoT Pi");
    cJSON_AddStringToObject(report, "model",  "1");
    cJSON_AddStringToObject(report, "vendor", "ACOINFO");
    
    str = cJSON_Print(root);
    sddc_return_value_if_fail(str, NULL);
    sddc_printf("REPORT DATA: %s\n", str);
    cJSON_Delete(root);

    return str;
}
/*
 * 创建 INVITE 数据
 */
static char *iot_pi_invite_data_create(void)
{
    cJSON *root;
    cJSON *report;
    char *str;

    root = cJSON_CreateObject();
    sddc_return_value_if_fail(root, NULL);

    report = cJSON_CreateObject();
    sddc_return_value_if_fail(report, NULL);

    cJSON_AddItemToObject(root, "report", report);
    cJSON_AddStringToObject(report, "name",   "IoT Pi");
    cJSON_AddStringToObject(report, "type",   "device");
    cJSON_AddBoolToObject(report,   "excl",   SDDC_FALSE);
    cJSON_AddStringToObject(report, "desc",   "翼辉 IoT Pi");
    cJSON_AddStringToObject(report, "model",  "1");
    cJSON_AddStringToObject(report, "vendor", "ACOINFO");

    str = cJSON_Print(root);
    sddc_return_value_if_fail(str, NULL);

    sddc_printf("INVITE DATA: %s\n", str);
    
    cJSON_Delete(root);

    return str;
}
/*
 * IO0 按键检测任务
 */
static void esp_io0_key_task()
{
    WiFi.disconnect();
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WIFI Wait for Smartconfig");
      
        // 设置为Station模式
        WiFi.mode(WIFI_STA);
      
        // 开始智能配网
        WiFi.beginSmartConfig();
        while (1)
        {
            delay(500);
            // 检查智能配网
            if (WiFi.smartConfigDone())
            {
                // 设置自动连接
                WiFi.setAutoConnect(true);  
                break;
            }
        }
    }
}
/*
 * 循环扫描按键
 */
static void esp_tick_task(void *arg)
{
    void *sddc = arg;
    (void)sddc;
    while(1)
    {
        button.tick();
        delay(100);
    }
}

void sensor_init()
{
    Serial2.begin(9600);
    Serial2.setDebugOutput(true);
    Serial2.println();
}

void setup() {
    byte mac[6];
    char *data;
    int ret;

    // Serial 串口是给和电脑通讯的，Serial2 是和传感器通讯的
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println();

    // 初始化传感器模块
    sensor_init();

  
    // 清除一下按键状态机的状态
    button.reset();
  
    // 创建按键扫描线程，长按 IO0 按键，松开后ESP32 将会进入 SmartConfig 模式
    sddc_printf("长按按键进入 Smartconfig...\n");
    button.attachLongPressStop(esp_io0_key_task);
    xTaskCreate(esp_tick_task, "button_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
  
    // 启动 WiFi 并且连接网络
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
        delay(500);
        Serial.print(".");
    }

    // 获取并打印 IP 地址
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("'ip :");
    Serial.print(WiFi.localIP());
    Serial.println("' to connect");   
  
    // 创建 SDDC 协议对象
    g_sddc = sddc_create(SDDC_CFG_PORT);
  
    // 设置事件响应函数
    sddc_set_on_message(g_sddc, iot_pi_on_message);                              // 设置接收消息请求时的回调函数
    sddc_set_on_message_ack(g_sddc, iot_pi_on_message_ack);                      // 设置接收消息确认时的回调函数
    sddc_set_on_message_lost(g_sddc, iot_pi_on_message_lost);                    // 设置丢失消息时的回调函数
    sddc_set_on_invite(g_sddc, iot_pi_on_invite);                                // 设置接受邀请请求时的回调函数
    sddc_set_on_invite_end(g_sddc, iot_pi_on_invite_end);                        // 设置发送邀请后的回调函数
    sddc_set_on_update(g_sddc, iot_pi_on_update);                                // 设置接收更新请求时的回调函数
    sddc_set_on_edgeros_lost(g_sddc, iot_pi_on_edgeros_lost);                    // 设置 EdgerOS 断连时的回调函数

    // 设置设备密码
#if SDDC_CFG_SECURITY_EN > 0                                                   // SDDC_CFG_SECURITY_EN 宏控制是否支持数据加密通信
    ret = sddc_set_token(g_sddc, "1234567890");
#endif 

    // 创建并设置 Report 报文数据
    data = iot_pi_report_data_create();
    sddc_return_if_fail(data);
    sddc_set_report_data(g_sddc, data, strlen(data));
  
    // 创建并设置 Invite 报文数据
    data = iot_pi_invite_data_create();
    sddc_return_if_fail(data);
    sddc_set_invite_data(g_sddc, data, strlen(data));
  
    // 获取并打印网卡 mac 地址
    WiFi.macAddress(mac);
    sddc_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
              mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
    // 使用网卡 mac 地址设置设备唯一标识 UID
    sddc_set_uid(g_sddc, mac);
}

void loop() {
    // 运行 SDDC 协议循环
    while (1) 
    {
        sddc_printf("SDDC running...\n");
        sddc_run(g_sddc);
        sddc_printf("SDDC quit!\n");
    }

    // 销毁 SDDC 协议
    sddc_destroy(g_sddc);
}
